/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.node;

import java.io.Serializable;
import java.util.Map;

import jp.aist.gtrc.plus.reserve.ReserveException;
import jp.aist.gtrc.plus.scheduler.status.ArchType;
import jp.aist.gtrc.plus.scheduler.status.OSType;
import jp.aist.gtrc.plus.scheduler.util.FakeHashMap;
import jp.aist.gtrc.plus.scheduler.util.StringHashMap;
import jp.aist.gtrc.plus.scheduler.util.TextUtil;

public class ResourceOption implements Serializable {

	private static final long serialVersionUID = -8906169243606119293L;

	/*
	 * We use FakeHashMap because db4o cannot handle HashMap correctly.
	 */
	private final FakeHashMap<String, String> options;
	private final FakeHashMap<String, String> extRscs;

	/*
	 * For generic options
	 */
	private static final String OPT_CPUNUM = "cpunum";	// # of cpu(core)
	private static final int DEFAULT_CPUNUM = 1;
	private static final String OPT_MEM = "mem";
	private static final long DEFAULT_MEM = 1;	// [byte]
	private static final String OPT_ARCH = "arch";
	private static final String OPT_OS = "os";
	private static final String OPT_PROCNUM = "procnum";	// # of launch Process/node
	private static final int DEFAULT_PROCNUM = 1;

	/*
	 * For SGE options
	 */
	private static final String OPT_PREPOSTQ = "prepostq";
	private static final String OPT_SLOTNUM = "slotnum";
	private static final int DEFAULT_SLOTNUM = 0;
	private static final String OPT_SGEQNAME = "sgeqname";
	private static final boolean MAKE_PREPOSTQ_DEFAULT = false;
	public static final String PROP_EXISTS = Boolean.toString(true);

	public ResourceOption() {

		options = new StringHashMap();
		extRscs = new StringHashMap();

	}

	private static void reloadMap(Map<String, String> mapDst,
			Map<String, String> mapSrc) {

		mapDst.clear();
		mapDst.putAll(mapSrc);

	}

	public void reloadOptions(ResourceOption newOpts) {

		reloadMap(this.options, newOpts.options);
		reloadMap(this.extRscs, newOpts.extRscs);

	}

	private String getOption(String attr) {

		return options.get(attr);

	}

	public void setNodeCPUNum(int num) throws ReserveException {

		if (num >= 0) {
			// NOTE: num==0 means downed/unreachable node status
			options.put(OPT_CPUNUM, Integer.toString(num));
		} else {
			throw new ReserveException("CPU number must be positive");
		}

	}

	public int getNodeCPUNum() {

		String n = getOption(OPT_CPUNUM);
		return (TextUtil.isValid(n))
			? Integer.parseInt(n) : DEFAULT_CPUNUM;

	}

	public void setNodePhysicalMemory(long memByte) throws ReserveException {

		if (memByte >= 0) {
			// NOTE: memByte==0 means downed/unreachable node status
			options.put(OPT_MEM, Long.toString(memByte));
		} else {
			throw new ReserveException("memory size must be positive");
		}

	}

	public long getNodePhysicalMemory() {

		String n = getOption(OPT_MEM);
		return (TextUtil.isValid(n))
			? Long.parseLong(n) : DEFAULT_MEM;

	}

	public void setArchType(ArchType arch) {

		if (arch != null)
			options.put(OPT_ARCH, arch.name());

	}

	public ArchType getArchType() {

		String s = getOption(OPT_ARCH);
		return TextUtil.isValid(s) ? ArchType.valueOf(s) : null;

	}

	public void setOSType(OSType os) {

		if (os != null)
			options.put(OPT_OS, os.name());

	}

	public OSType getOSType() {

		String s = getOption(OPT_OS);
		return TextUtil.isValid(s) ? OSType.getOS(s) : null;

	}

	public void setProcessNum(int num) throws ReserveException {

		if (num > 0) {
			options.put(OPT_PROCNUM, Integer.toString(num));
		} else {
			throw new ReserveException("process number must be positive");
		}

	}

	public int getProcessNum() {

		String n = options.get(OPT_PROCNUM);
		return (n != null) ? Integer.parseInt(n) : DEFAULT_PROCNUM;

	}

	public void setMakePrePostQueue(boolean use) {

		options.put(OPT_PREPOSTQ, Boolean.toString(use));

	}

	public boolean isMakePrePostQueue() {
		String s = getOption(OPT_PREPOSTQ);
		return TextUtil.isValid(s)
			? Boolean.valueOf(s) : MAKE_PREPOSTQ_DEFAULT;
	}

	public void setSlotNum(int num) {

		options.put(OPT_SLOTNUM, Integer.toString(num));

	}

	public int getSlotNum() {

		String s = getOption(OPT_SLOTNUM);
		return TextUtil.isValid(s) ? Integer.valueOf(s) : DEFAULT_SLOTNUM;

	}

	public void setSGEQueueName(String qName) {

		if (TextUtil.isValid(qName)) {
			options.put(OPT_SGEQNAME, qName);
		}

	}

	public String getSGEQueueName() {

		return TextUtil.toNonNull(getOption(OPT_SGEQNAME));

	}


	public void addResourceOptions(Map<String, String> rscMap) {

		reloadMap(extRscs, rscMap);

	}

	public void addResourceOptions(String[] reqProp) {

		if (reqProp == null)
			return;

		for (String req : reqProp) {
			String[] r = req.split("=");
			switch (r.length) {
			case 2:
				extRscs.put(r[0], r[1]);
				break;
			case 1:
				extRscs.put(req, PROP_EXISTS);
				break;
			default:
				// ignore
				break;
			}
		}

	}

	public Map<String, String> getDefaultOptions() {

		return options;

	}

	public Map<String, String> getResourceOptions() {

		return extRscs;

	}

	public String toString() {
		return "prop = " + TextUtil.fromMap(options, ",")
			+ ", extRscs = " + TextUtil.fromMap(extRscs, ",");
	}

	public boolean equals(Object o) {

		if (o instanceof ResourceOption) {
			ResourceOption other = (ResourceOption)o;
			if (this.options.equals(other.options) == false)
				return false;
			if (this.extRscs.equals(other.extRscs) == false)
				return false;
			return true;
		} else {
			assert(false);
			return false;
		}

	}

	public int hashCode() {
		return options.hashCode() + extRscs.hashCode();
	}

}
